Skip to content

Improve TLS certificate loading, handling and validation#478

Merged
kailan merged 8 commits intomainfrom
fdenis/cert-validation
Apr 24, 2026
Merged

Improve TLS certificate loading, handling and validation#478
kailan merged 8 commits intomainfrom
fdenis/cert-validation

Conversation

@jedisct1
Copy link
Copy Markdown
Contributor

Improved Certificate Loading:

  • Now fails explicitly when no certificates can be loaded
  • Properly reports when CA certificates are unavailable

Better Certificate Validation:

  • Certificate validation checks during connection establishment
  • Added basic validation of certificate data to ensure certificates aren't empty
  • Added proper error handling for invalid DNS names

Proper TLS Connection Validation:

  • Added explicit error handling for TLS connection failures
  • Added logging for certificate validation failures for easier debugging

1. Improved Certificate Loading:
  - Now fails explicitly when no certificates can be loaded
  - Properly reports when CA certificates are unavailable
2. Enhanced Error Handling:
  - Added new error types for specific TLS/certificate validation issues
  - Improved error messages for better diagnostics
3. Better Certificate Validation:
  - Implemented certificate validation checks during connection establishment
  - Added basic validation of certificate data to ensure certificates aren't
empty
  - Added proper error handling for invalid DNS names
4. Proper TLS Connection Validation:
  - Added explicit error handling for TLS connection failures
  - Added logging for certificate validation failures for easier debugging
@cceckman-at-fastly cceckman-at-fastly self-requested a review May 27, 2025 20:54
Copy link
Copy Markdown
Contributor

@cceckman-at-fastly cceckman-at-fastly left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Patched with some syntactic changes - map_err and use of the ?, mostly - to cut out some of the error-handling boilerplate.

@jedisct1 If this looks good to you, it's good to merge IMO!

Comment thread lib/src/upstream.rs Outdated
Comment thread src/upstream.rs
Comment on lines 49 to 70
let mut roots = rustls::RootCertStore::empty();
match rustls_native_certs::load_native_certs() {
Ok(certs) => {
let mut cert_added = false;
for cert in certs {
if let Err(e) = roots.add(&rustls::Certificate(cert.0)) {
warn!("failed to load certificate: {e}");
match roots.add(&rustls::Certificate(cert.0)) {
Ok(_) => cert_added = true,
Err(e) => {
// Log but continue trying other certs
warn!("failed to load certificate: {e}");
}
}
}
if !cert_added {
return Err(Error::TlsNoCertsAdded);
}
}
Err(err) => return Err(Error::BadCerts(err)),
}
if roots.is_empty() {
warn!("no CA certificates available");
return Err(Error::TlsNoCAAvailable);
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know some of this was pre-existing but I believe we can simplify to:

let certs = rustls_native_certs::load_native_certs().map_err(Error::BadCerts)?;
let mut roots = rustls::RootCertStore::empty();
let (added, failed) = roots.add_parsable_certificates(&certs.into_iter().map(|c| c.0).collect::<Vec<_>>());
if failed > 0 {
    warn!("failed to load {} certificate(s). attempting to continue with {} available certificate(s)", failed, added);
}
if roots.is_empty() {
    return Err(Error::TlsNoCAAvailable);
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also means the TlsNoCertsAdded error variant can be removed

Comment thread src/upstream.rs Outdated
Comment on lines +170 to +172
let tcp = connect_fut
.await
.map_err(|e| std::io::Error::other(format!("TCP connection error: {}", e)))?;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The HTTP connector already provides descriptive error messages so we probably don't need this extra wrapping

Suggested change
let tcp = connect_fut
.await
.map_err(|e| std::io::Error::other(format!("TCP connection error: {}", e)))?;
let tcp = connect_fut.await?

@kailan
Copy link
Copy Markdown
Member

kailan commented Apr 20, 2026

@jedisct1 I hope you don't mind that I took the liberty of reviving this pull request and taking advantage of some since-released Rust features that can simplify the error construction. I'll re-request review from both parties and hopefully we can ship your changes asap.

kailan
kailan previously approved these changes Apr 24, 2026
Copy link
Copy Markdown
Member

@kailan kailan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Chatted with @jedisct1 out of band – we're happy to go ahead with this considering Charles' earlier review.

@kailan kailan merged commit 9900324 into main Apr 24, 2026
14 checks passed
@kailan kailan deleted the fdenis/cert-validation branch April 24, 2026 17:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants